#! /usr/bin/python
# -*- coding: utf-8 -*-

###############################################################################
# Copyright [2010] [Roberto Carrasco - rcarrasco@talentus.cl]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. 
###############################################################################

"""Skytraq based GPS Monitor"""

from sys import path
from time import sleep

import pygtk
pygtk.require('2.0')
import gtk
import gobject

import AboutDialog
import ProgressWindow
import Skytraq
import BinaryResponse

class Main:
    def __init__(self):
        self.skytraq = Skytraq.Skytraq()
        self.about = AboutDialog.AboutDialog()
        self.pw = ProgressWindow.ProgressWindow()
        self.builder = gtk.Builder()
        self.builder.add_from_file('./UI/MainWindow.ui')
        self.builder.connect_signals(self)
        self.__setOnlyDigits('n_maxTime')
        self.__setOnlyDigits('n_minTime')
        self.__setOnlyDigits('n_maxDistance')
        self.__setOnlyDigits('n_minDistance')
        self.__setOnlyDigits('n_maxSpeed')
        self.__setOnlyDigits('n_minSpeed')
        self.__setOnlyDigits('n_dataLogging')
        self.__setOnlyDigits('n_logFifoMode') 
        self.__setTextCombobox('deviceSpeed', [4800, 9600, 19200, 38400, 57600, 115200], 1)
        self.builder.get_object('MainWindow').show_all()

    def quitApp(self, widget, data = None):
        self.skytraq.disconnect()
        gtk.main_quit()

    def showAbout(self, widget, data = None):
        self.about.show()

    def doConnectDevice(self, widget, data = None):
        def doTask():
            self.pw.begin()
            self.pw.append('Connecting ...')
            self.__setConfig()
            yield True
            device = self.__getText('devicePort')
            speed = self.__getTextCombobox('deviceSpeed')
            if self.skytraq.connect(device, int(speed), 5, self.nmea_observer):
                self.pw.append('OK\x0a')
                for t in self.__loadConfig():
                    yield True
                self.__setSensitive('devicePort', False)
                self.__setSensitive('deviceSpeed', False)
                self.__setSensitive('connect', False)
                self.__setSensitive('disconnect', True)
                self.__setSensitive('resetDevice', True)
                self.__setSensitive('factoryDefault', True)
                self.__setSensitive('saveConfig', True)
                self.__setSensitive('reload', True)
                self.__setSensitive('clearSectors', True)
                self.__setSensitive('retrieveSectors', True)
                self.__setTextView('wSectorsText', '')
                self.__setTextView('wNmeaText', '')
                self.pw.append('Configuring NMEA interval ...')
                if self.skytraq.sendConfigureNmea( 1, 1, 1, 1, 1, 1, 1, Skytraq.ATTRIBUTE_UPDATE_TO_SRAM):
                    self.pw.append('OK\x0a')
                else:
                    self.pw.append('Error\x0a')
            else:
                self.pw.append('Error\x0a')
            self.pw.end()
            yield False
        task = doTask()
        gobject.idle_add(task.next)

    def doDisconnectDevice(self, widget, data = None):
        def doTask():
            self.pw.begin()
            self.pw.append('Disconnecting ...')
            yield True
            self.skytraq.disconnect()
            self.__setSensitive('devicePort', True)
            self.__setSensitive('deviceSpeed', True)
            self.__setSensitive('connect', True)
            self.__setSensitive('disconnect', False)
            self.__setSensitive('resetDevice', False)
            self.__setSensitive('factoryDefault', False)
            self.__setSensitive('saveConfig', False)
            self.__setSensitive('reload', False)
            self.__setSensitive('clearSectors', False)
            self.__setSensitive('retrieveSectors', False)
            self.pw.append('OK\x0a')
            self.pw.end()
            yield False
        task = doTask()
        gobject.idle_add(task.next)

    def doResetDevice(self, widget, data = None):
        def doTask():
            self.pw.begin()
            self.pw.append('Resetting Device ...')
            yield True
            if self.skytraq.sendSystemRestart(Skytraq.START_MODE_COLDSTART).ack:
                self.pw.append('OK\x0a')
                for t in self.__loadConfig():
                    yield True
            else:
                self.pw.append('Error\x0a')
            self.pw.end()
            yield False
        task = doTask()
        gobject.idle_add(task.next)

    def doFactoryDefault(self, widget, data = None):
        def doTask():
            self.pw.begin()
            self.pw.append('Setting Factory Default values ...')
            yield True
            if self.skytraq.sendSetFactoryDefaults(True).ack :
                self.pw.append('OK\x0a')
                for t in self.__loadConfig():
                    yield True
            else:
                self.pw.append('Error\x0a')
            self.pw.end()
            yield False
        task = doTask()
        gobject.idle_add(task.next)

    def doReloadConfig(self, widget, data = None):
        def doTask():
            self.pw.begin()
            for t in self.__loadConfig():
                yield True
            self.pw.end()
            yield False
        task = doTask()
        gobject.idle_add(task.next)

    def doSaveConfig(self, widget, data = None):
        def doTask():
            self.pw.begin()
            self.pw.append('Saving Config values ...')
            yield True
            if self.skytraq.sendLogConfigureControl(
                    int(self.__getText('n_maxTime')),
                    int(self.__getText('n_minTime')),
                    int(self.__getText('n_maxDistance')),
                    int(self.__getText('n_minDistance')),
                    int(self.__getText('n_maxSpeed')),
                    int(self.__getText('n_minSpeed')),
                    int(self.__getText('n_dataLogging')),
                    int(self.__getText('n_logFifoMode'))).ack:
                self.pw.append('OK\x0a')
                for t in self.__loadConfig():
                    yield True
            else:
                self.pw.append('Error\x0a')
            self.pw.end()
            yield False
        task = doTask()
        gobject.idle_add(task.next)

    def doClearSectors(self, widget, data = None):
        def doTask():
              self.pw.begin()
              self.pw.append('Clearing Sectors ...')
              yield True
              if self.skytraq.sendLogClearControl().ack:
                self.pw.append('OK\x0a')
                for t in self.__loadConfig():
                    yield True
              else:
                self.pw.append('Error\x0a')
              self.pw.end()
              yield False
        task = doTask()
        gobject.idle_add(task.next)

    def doRetrieveSectors(self, widget, data=None):
        def doTask():
            self.pw.begin()
            self.__setTextView('wSectorsText', '')
            yield True
            usedSectors = self.totalSectors - self.sectorsLeft + 1
            sectors = bytearray()
            nsectors = 0
            for s in range(0, usedSectors):
                self.pw.append('Reading sector %d/%d ... ' % (s + 1,usedSectors))
                yield True
                message = self.skytraq.sendLogSectorReadControl(s)
                sleep(0.1)
                if message.nack:
                    self.pw.append('NACK\x0a')
                elif message.timeout:
                    self.pw.append('TIMEOUT\x0a')
                elif message.response is None:
                    self.pw.append('No response\x0a')
                else:
                   nsectors = nsectors+1
                   sectors.extend(message.response.sector)
                   self.pw.append('OK\x0a')
            if nsectors == usedSectors:
                self.pw.append('Processing Sectors ...\x0a')
                yield True
                points = BinaryResponse.parseSectors(sectors)
                self.pw.append('Converting to GPX ...\x0a')
                yield True
                gpx = BinaryResponse.toGPX(points)
                self.__appendTextView('wSectorsText', gpx)
                self.pw.append('OK\x0a')
            self.pw.end()
            yield False
        task = doTask()
        gobject.idle_add(task.next)

    def doSaveSectors(self, widget, data=None):
        dialog = gtk.FileChooserDialog(title = 'Save GPX Data',
                                       action = gtk.FILE_CHOOSER_ACTION_SAVE,
                                       buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE,gtk.RESPONSE_OK)
                                      )
        dialog.set_current_name('.gpx')
        dialog.set_do_overwrite_confirmation(True)
        filter1 = gtk.FileFilter()
        filter1.set_name('GPX Files')
        filter1.add_mime_type('application/gpx-xml')
        #filter1.add_mime_type('application/octet-stream')
        filter1.add_pattern('*.gpx')
        dialog.add_filter(filter1)
        filter2 = gtk.FileFilter()
        filter2.set_name('All Files')
        filter2.add_pattern('*')
        dialog.add_filter(filter2)
        while True:
            response=dialog.run()
            if response == gtk.RESPONSE_OK:
                fname = dialog.get_filename()
                fd = None
                try:
                    fd = open(fname , 'wb')
                    fd.write(self.__getTextView('wSectorsText'))
                    fd.close()
                    dialog.destroy()
                    break
                except:
                    errDlg = gtk.MessageDialog(parent = None, flags = 0, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_OK, message_format='Error writing GPX data')
                    errDlg.run()
                    errDlg.destroy()
                if fd:
                    fd.close()
            else:
                dialog.destroy()
                break;

    def nmea_observer(self, nmea):
        def doTask():
            self.__appendTextView('wNmeaText', str(nmea.message))
            if nmea.messageId == '$GPGGA':
                self.__setText('gpggaUtcTime', nmea.utcTime)
                self.__setText('gpggaLatitude', nmea.latitude)
                self.__setText('gpggaLongitude', nmea.longitude)
                self.__setText('gpggaPositionFix', nmea.positionFix)
                self.__setText('gpggaSatellitesUsed', nmea.satellitesUsed)
                self.__setText('gpggaHdop', nmea.hdop)
                self.__setText('gpggaMslAltitude', nmea.mslAltitude)
                self.__setText('gpggaGeoidSeparation', nmea.geoidSeparation)
                self.__setText('gpggaAgeOfDifferentialCorrections', nmea.ageOfDifferentialCorrections)
                self.__setText('gpggaDiffReferenceCorrections', nmea.diffReferenceCorrections)
            elif nmea.messageId == '$GPGLL':
                self.__setText('gpgllLatitude',nmea.latitude)
                self.__setText('gpgllLongitude', nmea.longitude)
                self.__setText('gpgllUtcTime', nmea.utcTime)
                self.__setText('gpgllStatus', nmea.status)
                self.__setText('gpgllModeIndicator', nmea.modeIndicator)
            elif nmea.messageId == '$GPGSA':
                self.__setText('gpgsaMode', nmea.mode)
                self.__setText('gpgsaFix', nmea.fix)
                self.__setText('gpgsaSvOnChannel1', nmea.svOnChannel[0])
                self.__setText('gpgsaSvOnChannel2', nmea.svOnChannel[1])
                self.__setText('gpgsaSvOnChannel3', nmea.svOnChannel[2])
                self.__setText('gpgsaSvOnChannel4', nmea.svOnChannel[3])
                self.__setText('gpgsaSvOnChannel5', nmea.svOnChannel[4])
                self.__setText('gpgsaSvOnChannel6', nmea.svOnChannel[5])
                self.__setText('gpgsaSvOnChannel7', nmea.svOnChannel[6])
                self.__setText('gpgsaSvOnChannel8', nmea.svOnChannel[7])
                self.__setText('gpgsaSvOnChannel9', nmea.svOnChannel[8])
                self.__setText('gpgsaSvOnChannel10', nmea.svOnChannel[9])
                self.__setText('gpgsaSvOnChannel11', nmea.svOnChannel[10])
                self.__setText('gpgsaSvOnChannel12', nmea.svOnChannel[11])
                self.__setText('gpgsaPdop', nmea.pdop)
                self.__setText('gpgsaHdop', nmea.hdop)
                self.__setText('gpgsaVdop', nmea.vdop)
            elif nmea.messageId == '$GPGSV':
                if nmea.messageNumber ==1:
                    self.__setText('gpgsvSatellitesInView', str(nmea.satellitesInView))
                    for i in range(0, 12):
                        idx=str(i + 1)
                        self.__setText('gpgsvSatelliteId' + idx , '')
                        self.__setText('gpgsvElevation' + idx , '')
                        self.__setText('gpgsvAzimuth' + idx , '')
                        self.__setText('gpgsvSnr' + idx , '')
                for i in range(0, 4):
                  idx = str(i + 1 + (nmea.messageNumber - 1) * 4)
                  s = nmea.satellites[i]
                  self.__setText('gpgsvSatelliteId' + idx , s.satelliteId)
                  self.__setText('gpgsvElevation' + idx , s.elevation)
                  self.__setText('gpgsvAzimuth' + idx , s.azimuth)
                  self.__setText('gpgsvSnr' + idx , s.snr)
            elif nmea.messageId == '$GPRMC':
                self.__setText('gprmcUtcTime', nmea.utcTime)
                self.__setText('gprmcStatus', nmea.status)
                self.__setText('gprmcLatitude', nmea.latitude)
                self.__setText('gprmcLongitude', nmea.longitude)
                self.__setText('gprmcSpeedOverGround', nmea.speedOverGround)
                self.__setText('gprmcCourseOverGround', nmea.courseOverGround)
                self.__setText('gprmcDate', nmea.date)
                self.__setText('gprmcMagneticVariation', nmea.magneticVariation)
                self.__setText('gprmcMode', nmea.mode)
            elif nmea.messageId == '$GPVTG':
                self.__setText('gpvtgCourseTrue', nmea.courseTrue)
                self.__setText('gpvtgReferenceTrue', nmea.referenceTrue)
                self.__setText('gpvtgCourseMagnetic', nmea.courseMagnetic)
                self.__setText('gpvtgReferenceMagnetic', nmea.referenceMagnetic)
                self.__setText('gpvtgSpeedInKnots', nmea.speedInKnots)
                self.__setText('gpvtgUnitsKnots', nmea.unitsKnots)
                self.__setText('gpvtgSpeedInKmh', nmea.speedInKmh)
                self.__setText('gpvtgUnitsKmh', nmea.unitsKmh)
                self.__setText('gpvtgMode', nmea.mode)
            else:
                pass
            yield False
        task = doTask()
        gobject.idle_add(task.next)

    def __loadConfig(self):
        self.pw.append('Getting Software Version ... ')
        self.__setConfig()
        yield True
        version = self.skytraq.sendQuerySoftwareVersion(Skytraq.SOFTWARE_TYPE_SYSTEM_CODE).response
        if version:
            self.pw.append('OK\x0a')
        else:
            self.pw.append('Error\x0a')
        self.pw.append('Getting Software CRC ...')
        yield True
        crc = self.skytraq.sendQuerySoftwareCRC(Skytraq.SOFTWARE_TYPE_SYSTEM_CODE).response
        if crc:
            self.pw.append('OK\x0a')
        else:
            self.pw.append('Error\x0a')
        self.pw.append('Getting Status Control ...')
        yield True
        status = self.skytraq.sendLogStatusControl().response
        if status:
            self.pw.append('OK\x0a')
        else:
            self.pw.append('Error\x0a')
        self.__setConfig(version, crc, status)

    def __setConfig(self, version = None, crc = None, status = None):
        if version:
            self.__setText('kernelVersion', version.kernelVersion)
            self.__setText('odmVersion', version.odmVersion)
            self.__setText('revisionNumber', version.revisionNumber)
        else:
            self.__setText('kernelVersion', '')
            self.__setText('odmVersion', '')
            self.__setText('revisionNumber', '')
        if crc:
            self.__setText('softwareCRC', str(crc.softwareCRC))
        else:
            self.__setText('softwareCRC', '')
        if status:
            self.totalSectors = status.totalSectors
            self.sectorsLeft = status.sectorsLeft
            self.__setText('currentLogBufferAddress', str(status.currentLogBufferAddress))
            self.__setText('sectorsLeft', str(status.sectorsLeft))
            self.__setText('totalSectors', str(status.totalSectors))
            self.__setText('maxTime', str(status.maxTime))
            self.__setText('minTime', str(status.minTime))
            self.__setText('maxDistance',str( status.maxDistance))
            self.__setText('minDistance', str(status.minDistance))
            self.__setText('maxSpeed', str(status.maxSpeed))
            self.__setText('minSpeed', str(status.minSpeed))
            self.__setText('dataLogging', str(status.dataLogging))
            self.__setText('logFifoMode', str(status.logFifoMode))
            self.__setText('n_maxTime', str(status.maxTime))
            self.__setText('n_minTime', str(status.minTime))
            self.__setText('n_maxDistance', str(status.maxDistance))
            self.__setText('n_minDistance', str(status.minDistance))
            self.__setText('n_maxSpeed', str(status.maxSpeed))
            self.__setText('n_minSpeed', str(status.minSpeed))
            self.__setText('n_dataLogging', str(status.dataLogging))
            self.__setText('n_logFifoMode', str(status.logFifoMode))
        else:
            self.totalSectors = 0
            self.sectorsLeft = 0
            self.__setText('currentLogBufferAddress', '')
            self.__setText('sectorsLeft', '')
            self.__setText('totalSectors', '')
            self.__setText('maxTime', '')
            self.__setText('minTime', '')
            self.__setText('maxDistance', '')
            self.__setText('minDistance', '')
            self.__setText('maxSpeed', '')
            self.__setText('minSpeed', '')
            self.__setText('dataLogging', '')
            self.__setText('logFifoMode', '')
            self.__setText('n_maxTime', '')
            self.__setText('n_minTime', '')
            self.__setText('n_maxDistance', '')
            self.__setText('n_minDistance', '')
            self.__setText('n_maxSpeed', '')
            self.__setText('n_minSpeed', '')
            self.__setText('n_dataLogging', '')
            self.__setText('n_logFifoMode', '')
      
    def __setOnlyDigits(self, field):
        def OnlyDigits(w, *args):
            text = w.get_text().strip()
            try:
                num = abs(int(float(text)))
                text = str(num)
            except:
                text = '0'
            w.set_text(text)
        widget = self.builder.get_object(field)
        widget.set_text('0')
        widget.connect('focus_out_event', OnlyDigits)

    def __setText(self, field, text):
        self.builder.get_object(field).set_text(text)

    def __getText(self, field):
        return self.builder.get_object(field).get_text()

    def __setTextView(self, field, text):
        textView = self.builder.get_object(field)
        textBuffer = textView.get_buffer()
        textBuffer.set_text(text)

    def __getTextView(self, field):
        textView = self.builder.get_object(field)
        textBuffer = textView.get_buffer()
        return textBuffer.get_text(textBuffer.get_start_iter(), textBuffer.get_end_iter())

    def __appendTextView(self, field, text):
        textView = self.builder.get_object(field)
        textBuffer = textView.get_buffer()
        textBuffer.insert(textBuffer.get_end_iter(), text)
        textView.scroll_to_iter(textBuffer.get_end_iter(), 0)

    def __getTextCombobox(self, field):
        combobox = self.builder.get_object(field)
        model = combobox.get_model()
        active = combobox.get_active()
        if active < 0:
            return None
        else:
            return model[active][0]

    def __setTextCombobox(self, field, entries, active):
        combobox = self.builder.get_object(field)
        liststore = gtk.ListStore(gobject.TYPE_STRING)
        for entry in entries:
            liststore.append([entry])
        combobox.set_model(liststore)
        cell = gtk.CellRendererText()
        combobox.pack_start(cell, True)
        combobox.add_attribute(cell, 'text', 0) 
        combobox.set_active(active)
        
    def __setSensitive(self, field, sensitive):
        self.builder.get_object(field).set_sensitive(sensitive)

if __name__ == '__main__':
    gobject.threads_init()
    Main()
    gtk.main()
